package top.codef.money;

import static java.util.stream.Collectors.toList;

import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.money.CurrencyUnit;
import javax.money.Monetary;
import javax.money.MonetaryAmount;
import javax.money.format.AmountFormatContext;

import org.apache.commons.lang3.StringUtils;
import org.javamoney.moneta.FastMoney;
import org.javamoney.moneta.format.CurrencyStyle;

import top.codef.MercuryException;
import top.codef.money.interfaces.MoneyPaser;

public abstract class MercuryMoneyParser implements MoneyPaser {

	private final AmountFormatContext amountFormatContext;

	private boolean strictCurrency = false;

	public MercuryMoneyParser(AmountFormatContext amountFormatContext) {
		this.amountFormatContext = amountFormatContext;
	}

	public boolean isStrictCurrency() {
		return strictCurrency;
	}

	public void setStrictCurrency(boolean strictCurrency) {
		this.strictCurrency = strictCurrency;
	}

	public AmountFormatContext getAmountFormatContext() {
		return amountFormatContext;
	}

	@Override
	public <T extends MonetaryAmount> T parse(String text) {
		boolean isUnderZero = text.indexOf("-") != -1;
		if (isUnderZero)
			text = text.replace("-", "");
		List<String> list = Arrays.stream(text.split(" ")).filter(x -> StringUtils.isNoneBlank(x)).collect(toList());
		String num = text;
		String pattern = "-?[0-9\\,]+(\\.[0-9]*)?";
		if (list.size() > 1) {
			num = list.stream().filter(x -> x.matches(pattern)).findFirst().orElseGet(null);
		} else {
			Matcher matcher = Pattern.compile("-?[0-9\\,]+(\\.[0-9]*)?").matcher(text);
			num = matcher.find() ? matcher.group() : null;
		}
		if (num != null) {
			String currency = text.replace(num, "").trim();
			try {
				num = isUnderZero ? "-" + num : num;
				Number number = DecimalFormat.getInstance().parse(num);
				CurrencyUnit unit = parseCurrencyUnit(currency);
				FastMoney fastMoney = FastMoney.of(number, unit);
				return from(fastMoney);
			} catch (Exception e) {
				throw new MercuryException("货币解析错误：", e);
			}
		}
		return null;
	}

	protected abstract <T extends MonetaryAmount> T from(MonetaryAmount monetaryAmount);

	private CurrencyUnit parseCurrencyUnit(String currency) {
		CurrencyStyle currencyStyle = amountFormatContext.get(CurrencyStyle.class);
		currencyStyle = currencyStyle == null ? CurrencyStyle.CODE : currencyStyle;
		CurrencyUnit cur = null;
		try {
			cur = Monetary.getCurrency(currency);
		} catch (Exception e) {
			if (!strictCurrency)
				cur = Monetary.getCurrency(amountFormatContext.getLocale());
			else
				throw new MercuryException("无法解析货币单位", e);
		}
		return cur;
	}
}
